package com.orm.analysis.dao;

import com.orm.analysis.model.Address;
import com.orm.analysis.model.ContactInfo;
import com.orm.analysis.model.Customer;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.*;
import javax.persistence.criteria.*;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

public abstract class GenericJPADAO<T, ID extends Serializable> {

    private Class<T> persistentClass;
    
    protected EntityManager em;

    protected EntityManager emPerf;


    @PersistenceContext(unitName = "HIBERNATEJPAORM")
    public void setEntityManager(EntityManager em) {
        this.em = em;
    }

    public EntityManager getEntityManager() {
        return em;
    }


    @PersistenceContext(unitName = "PERF_ORM")
    public void setEntityManagerPerf(EntityManager emPerf) {
        this.emPerf = emPerf;
    }

    public EntityManager getEntityManagerPerf() {
        return emPerf;
    }


    //    @Transactional
    public void save(T entity) {
        getEntityManager().persist(entity);
    }

//    @Transactional
      public void saveList(List<T> entities) {
          for(T entity: entities){
              getEntityManager().persist(entity);
          }

      }

//    @Transactional
    public void update(T entity) {
           getEntityManager().merge(entity);
    }

//    @Transactional
    public void delete(Object id) {
        getEntityManager().remove(getEntityManager().find(persistentClass, id));

     }


    public T getById(Object id) {
        return getEntityManager().find(persistentClass, id);
    }


    public T findWithDepth(Class<T> type, String pkFieldName, Object id, String... fetchRelations) {
            CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
            CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(type);
            Root<T> root = criteriaQuery.from(type);

            for (String relation : fetchRelations) {
                FetchParent<T, T> fetch = root;
                System.out.println(">>>>>"+relation);
                for (String pathSegment : relation.split("[.]")) {

                    fetch = fetch.fetch(pathSegment, JoinType.LEFT);
                }
            }

            criteriaQuery.where(criteriaBuilder.equal(root.get(pkFieldName), id));

            return getSingleOrNoneResult(getEntityManager().createQuery(criteriaQuery));
        }

    private <T> T getSingleOrNoneResult(TypedQuery<T> query) {
            query.setMaxResults(1);
            List<T> result = query.getResultList();
            if (result.isEmpty()) {
                return null;
            }

            return result.get(0);
    }

    public List<T>  findAllWithDepth(Class<T> type,  String... fetchRelations) {
        CriteriaBuilder criteriaBuilder = getEntityManager().getCriteriaBuilder();
        CriteriaQuery<T> criteriaQuery = criteriaBuilder.createQuery(type);
        Root<T> root = criteriaQuery.from(type);

        for (String relation : fetchRelations) {
            FetchParent<T, T> fetch = root;
            System.out.println(">>>>>" + relation);
            for (String pathSegment : relation.split("[.]")) {

                fetch = fetch.fetch(pathSegment, JoinType.LEFT);
            }
        }


        return getEntityManager().createQuery(criteriaQuery).getResultList();
    }


    public List<T> listByFields(StringBuffer queryString, String... fieldsNamesValues) {
        Query query = this.em.createQuery(queryString.toString());
        if (fieldsNamesValues.length > 0) {
            queryString.append("where ");
        }

        for (String relation : fieldsNamesValues) {

            String[] fieldsNameValue = relation.split("[,]");
            queryString.append("and " + fieldsNameValue[0] + "=:" + fieldsNameValue[0]);
            query = this.em.createQuery(queryString.toString());
            query.setParameter(fieldsNameValue[0], fieldsNameValue[1]);

        }


        return query.getResultList();
    }


    public int updateExecuteUpdateByQuery(String queryString){

        Query query =  this.em.createQuery(queryString);
        int updateCount =  query.executeUpdate();
        return updateCount;
    }
}
